#include "Application.h"
#include "Tools.h"

static enum _AppState   state      = APP_STATE_INIT;
static enum _AppEvent   event      = APP_EVENT_NONE;
static enum _ButtonType button     = BUTTON_TYPE_NONE;
static enum _ButtonType operate[2] = {BUTTON_TYPE_NONE, BUTTON_TYPE_NONE};

static void
application_catch_event (void)
{
        switch (button) {
        case BUTTON_TYPE_NONE:
                event = APP_EVENT_NONE;
                break;
        case BUTTON_TYPE_DOT:
                event = APP_EVENT_DOT;
                break;
        case BUTTON_TYPE_CLS:
                event = APP_EVENT_CLEAN;
                break;
        case BUTTON_TYPE_EQU:
                event = APP_EVENT_EQU;
                break;
        case BUTTON_TYPE_DIV:
        case BUTTON_TYPE_MUL:
        case BUTTON_TYPE_SUB:
        case BUTTON_TYPE_ADD:
                event = APP_EVENT_OP;
                break;
        case BUTTON_TYPE_LEFT:
                event = APP_EVENT_LEFT;
                break;
        case BUTTON_TYPE_RIGHT:
                event = APP_EVENT_RIGHT;
                break;
        default:
                event = APP_EVENT_NUM_BTN;
                break;
        }
}

static void
application_state_change (enum _AppState _st)
{
        if (state != APP_STATE_WAIT_RECOVER)
                state = _st;
        else if (state == APP_STATE_WAIT_RECOVER && _st == APP_STATE_INIT)
                state = _st;
}

static void
application_recover (void)
{
        show[7] = 0x0;
        show[6] = 0x0;
        show[5] = 0x0;
        show[4] = 0x79;
        show[3] = 0x50;
        show[2] = 0x50;
        show[1] = 0x5c;
        show[0] = 0x50;
}

static void
application_change_number_to_decimal (void)
{
        if (num1.type != N_TYPE_DECIMAL) {
                num1.number.f = (decimal_t)num1.number.l;
                num1.type     = N_TYPE_DECIMAL;
        }
        if (num2.type != N_TYPE_DECIMAL) {
                num2.number.f = (decimal_t)num2.number.l;
                num2.type     = N_TYPE_DECIMAL;
        }
}

static void
application_calculate (void)
{
        switch (operate[0]) {
        case BUTTON_TYPE_ADD:
                if (num1.type == N_TYPE_INTEGER &&
                    num2.type == N_TYPE_INTEGER) {
                        num1.number.l += num2.number.l;
                } else {
                        application_change_number_to_decimal ();
                        num1.number.f += num2.number.f;
                }
                break;
        case BUTTON_TYPE_SUB:
                if (num1.type == N_TYPE_INTEGER &&
                    num2.type == N_TYPE_INTEGER) {
                        num1.number.l = num2.number.l - num1.number.l;
                } else {
                        application_change_number_to_decimal ();
                        num1.number.f = num2.number.f - num1.number.f;
                }
                break;
        case BUTTON_TYPE_MUL:
                if (num1.type == N_TYPE_INTEGER &&
                    num2.type == N_TYPE_INTEGER) {
                        num1.number.l *= num2.number.l;
                } else {
                        application_change_number_to_decimal ();
                        num1.number.f *= num2.number.f;
                }
                break;
        case BUTTON_TYPE_DIV:
                if (num1.type == N_TYPE_INTEGER || num2.type == N_TYPE_INTEGER)
                        application_change_number_to_decimal ();
                if (num1.number.f == 0.0) {
                        application_recover ();
                        application_state_change (APP_STATE_WAIT_RECOVER);
                        break;
                }
                num1.number.f = num2.number.f / num1.number.f;
                break;
        default:
                break;
        }
}

static void
application_action_for_event_clean (void)
{
        num1.number.f  = 0.0;
        num1.type      = N_TYPE_INTEGER;
        num2.number.f  = 0.0;
        num2.type      = N_TYPE_INTEGER;
        multiple_digit = 0.0;
        operate[0]     = BUTTON_TYPE_NONE;
        operate[1]     = BUTTON_TYPE_NONE;
}

void
main (void)
{
        num1.number.f = 0.0;
        num1.type     = N_TYPE_INTEGER;
        for (i = 0; i < 8; ++i)
                show[i] = 0x0d;
        offset = 11;
        n_buffer_format ();
        monitor_refresh_cache (1);

loop:
        monitor_push ();
        button = button_get_type ();
        if (button == BUTTON_TYPE_NONE)
                goto loop;

        application_catch_event ();

        if (event == APP_EVENT_LEFT) {
                if (offset > 7) {
                        offset -= 1;
                        _dotpot -= 1;
                }
                monitor_refresh_cache (0);
                monitor_push ();
                goto loop;
        } else if (event == APP_EVENT_RIGHT) {
                if (offset < 11) {
                        offset += 1;
                        _dotpot += 1;
                }
                monitor_refresh_cache (0);
                monitor_push ();
                goto loop;
        }

        switch (state) {
        case APP_STATE_INIT:
                if (event == APP_EVENT_NUM_BTN) {
                        number_pull ((uint8)button);
                        application_state_change (APP_STATE_WAIT_NUMBER);
                } else if (event == APP_EVENT_DOT) {
                        if (num1.type == N_TYPE_INTEGER) {
                                num1.number.f = (decimal_t)num1.number.l;
                                num1.type     = N_TYPE_DECIMAL;
                        }
                        application_state_change (APP_STATE_WAIT_DECIMAL);
                } else if (event == APP_EVENT_OP) {
                        operate[0] = operate[1];
                        operate[1] = button;
                        application_calculate ();
                        application_state_change (APP_STATE_GET_OPERATOR);
                }
                break;
        case APP_STATE_WAIT_DECIMAL:
                if (event == APP_EVENT_NUM_BTN) {
                        number_pull ((uint8)button);
                        application_state_change (APP_STATE_WAIT_NUMBER);
                } else if (event == APP_EVENT_OP || event == APP_EVENT_EQU) {
                        application_recover ();
                        application_state_change (APP_STATE_WAIT_RECOVER);
                } else if (event == APP_EVENT_CLEAN) {
                        application_action_for_event_clean ();
                        application_state_change (APP_STATE_INIT);
                }
                break;
        case APP_STATE_WAIT_NUMBER:
                if (event == APP_EVENT_NUM_BTN) {
                        number_pull ((uint8)button);
                        application_state_change (APP_STATE_WAIT_NUMBER);
                } else if (event == APP_EVENT_DOT) {
                        if (num1.type == N_TYPE_INTEGER) {
                                num1.number.f = (decimal_t)num1.number.l;
                                num1.type     = N_TYPE_DECIMAL;
                        }
                        application_state_change (APP_STATE_WAIT_DECIMAL);
                } else if (event == APP_EVENT_OP) {
                        operate[0] = operate[1];
                        operate[1] = button;
                        application_calculate ();
                        application_state_change (APP_STATE_GET_OPERATOR);
                } else if (event == APP_EVENT_EQU) {
                        operate[0] = operate[1];
                        operate[1] = BUTTON_TYPE_NONE;
                        application_calculate ();
                        application_state_change (APP_STATE_GET_EQU);
                } else if (event == APP_EVENT_CLEAN) {
                        application_action_for_event_clean ();
                        application_state_change (APP_STATE_INIT);
                }
                break;
        case APP_STATE_GET_OPERATOR:
                if (event == APP_EVENT_NUM_BTN) {
                        num2.number.f  = num1.number.f;
                        num2.type      = num1.type;
                        num1.number.f  = 0.0;
                        num1.type      = N_TYPE_INTEGER;
                        multiple_digit = 0.0;
                        number_pull ((uint8)button);
                        application_state_change (APP_STATE_WAIT_NUMBER);
                } else if (event == APP_EVENT_DOT || event == APP_EVENT_EQU) {
                        application_recover ();
                        application_state_change (APP_STATE_WAIT_RECOVER);
                } else if (event == APP_EVENT_OP) {
                        operate[1] = button;
                } else if (event == APP_EVENT_CLEAN) {
                        application_action_for_event_clean ();
                        application_state_change (APP_STATE_INIT);
                }
                break;
        case APP_STATE_WAIT_RECOVER:
                if (event == APP_EVENT_CLEAN) {
                        application_action_for_event_clean ();
                        application_state_change (APP_STATE_INIT);
                }
                break;
        case APP_STATE_GET_EQU:
                if (event == APP_EVENT_NUM_BTN) {
                        num1.number.f  = 0.0;
                        num1.type      = N_TYPE_INTEGER;
                        num2.number.f  = 0.0;
                        num2.type      = N_TYPE_INTEGER;
                        multiple_digit = 0.0;
                        number_pull ((uint8)button);
                        application_state_change (APP_STATE_WAIT_NUMBER);
                } else if (event == APP_EVENT_DOT) {
                        num1.number.f  = 0.0;
                        num1.type      = N_TYPE_INTEGER;
                        num2.number.f  = 0.0;
                        num2.type      = N_TYPE_INTEGER;
                        multiple_digit = 0.0;
                        if (num1.type == N_TYPE_INTEGER) {
                                num1.number.f = (decimal_t)num1.number.l;
                                num1.type     = N_TYPE_DECIMAL;
                        }
                        application_state_change (APP_STATE_WAIT_DECIMAL);
                } else if (event == APP_EVENT_OP) {
                        operate[1] = button;
                        application_state_change (APP_STATE_GET_OPERATOR);
                } else if (event == APP_EVENT_CLEAN) {
                        application_action_for_event_clean ();
                        application_state_change (APP_STATE_INIT);
                }
                break;
        default:
                break;
        }

        if (state != APP_STATE_WAIT_RECOVER) {
                n_buffer_format ();
                monitor_refresh_cache (1);
        }

        goto loop;
}
